# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: api\handlers\status\dependency_server.py
__author__ = "sanyi"
import logging, os.path, time, hmac, hashlib, uuid
from sqlalchemy import select as sql_select
from settings_provider import settings
from api.handlers import ResourceHandler
from helpers.encryption.file_aes_cbc import decrypt_file_generator
from server.http_responses.http_status_codes import Http200File
from db.tables.uploads import UploadRow
from helpers.hashing import sha256
from server.http_responses.http_errors import Http401, Http404
from db import Connection

_logger = logging.getLogger("api.handlers.scan.dependency_server")


class DependencyServer(ResourceHandler):
    DEPENDENCY_TARGET_UPLOADS = "u"
    DEPENDENCY_SCAN_STATUS_DB = "s"

    def __init__(self, current_shard="master", **_):
        self.current_shard = current_shard

    def download_dependency(self, dependency_data, **_):
        """
        dependency_data:
            type: 1
            random: 7
            expiration: 8
            dependency: any
            signature: 64
        """
        _logger.debug("download_dependency: %s", dependency_data)
        dependency_type = dependency_data[0]
        expiration = int(dependency_data[8:16], 16)
        if time.time() > expiration:
            _logger.warning("signature expired")
            raise Http401()
        request_signature = dependency_data[-64:]
        uploads_salt = settings.get("uploads_salt", "")
        signature = (
            hmac.new(
                uploads_salt.encode(),
                dependency_data[0:-64].encode(),
                digestmod=hashlib.sha256,
            )
        ).hexdigest()
        if signature != request_signature:
            _logger.warning("signature invalid")
            raise Http401()
        dependency = dependency_data[16:-64]
        decrypt_secret = False
        if dependency_type == self.DEPENDENCY_TARGET_UPLOADS:
            dependency_id = str(uuid.UUID(dependency))
            with Connection(self.current_shard) as (connection):
                q = (
                    sql_select((UploadRow,))
                    .where(UploadRow.upload_id == dependency_id)
                    .where(UploadRow.deleted_at.is_(None))
                )
                upload = connection.execute(q).fetchone()
                if not upload:
                    _logger.debug("target upload not found")
                    return Http404()
                upload = dict(upload)
            file_path = os.path.join(settings["storage.targets"], dependency_id)
            file_name = upload["name"]
            if settings.get("uploads_salt"):
                decrypt_secret = sha256(
                    upload["owner_id"] + dependency_id, settings.get("uploads_salt")
                )
            else:
                if dependency_type == self.DEPENDENCY_SCAN_STATUS_DB:
                    _logger.debug("scan status db: %s", dependency_data)
                    dependency = dependency.split(".", 1)
                    if len(dependency) != 2:
                        _logger.warning(
                            "invalid dependency for DEPENDENCY_SCAN_STATUS_DB"
                        )
                        raise Http404()
                    scanning_app, scan_session_id = dependency
                    scan_state_storage = settings.get("storage.scan_state_db")
                    if not scan_state_storage:
                        _logger.warning("scan_state_storage is not defined")
                        return Http404()
                    file_path = os.path.join(
                        settings["storage.scan_state_db"], scanning_app, scan_session_id
                    )
                    file_name = scan_session_id
                else:
                    return Http404()
                if not os.path.exists(file_path):
                    _logger.debug("scan state db not found: %s", file_path)
                    return Http404()

                def file_reader():
                    with open(file_path, "rb") as (f):
                        while True:
                            d = f.read(8192)
                            if d:
                                yield d
                            else:
                                break

                if not decrypt_secret:
                    return Http200File(file_name, file_reader())
                file_header = open(file_path, "rb").read(8)
                if file_header != b"Salted__":
                    return Http200File(file_name, file_reader())
                g = decrypt_file_generator(file_path, decrypt_secret)
                return Http200File(file_name, g)
